﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
using Fuse8.ContentManagementFramework.Web.ApplicationRuntime;
using Fuse8.DomainFramework.Common;

using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.Web.StartUp;
using Fuse8.ContentManagementFramework.SystemFacade.Utilities;
using Fuse8.ContentManagementFramework.SystemFacade;

namespace Fuse8.ContentManagementFramework.Web.MvcPowered
{
    internal static class CmsMvcPageControlFactory
    {
        public static void ExecuteControl(string regionName, CmsPageControl pageControl, TextWriter writer, HtmlHelper helper)
        {
            Guard.ArgumentNotEmpty(regionName, "regionName");
            Guard.ArgumentNotNull(pageControl, "pageControl");
            Guard.ArgumentNotNull(writer, "writer");
            Guard.ArgumentNotNull(helper, "helper");

            try
            {
                switch (pageControl.CommonPageControlType)
                {
                    case PageControlTypeKind.TemplateRegionPreview:
                        RenderTemplateRegionPreview(regionName, writer);
                        break;
                    case PageControlTypeKind.Content:
                    case PageControlTypeKind.Snippet:
                        RenderContent(pageControl.PageControlContent, writer);
                        break;
                    case PageControlTypeKind.Media:
                        //RenderMvcMedia(pageControl.PageControlValue, writer);
                        break;
                    case PageControlTypeKind.MvcPartial:                    
                        var mvcPartialParameters = pageControl.GetCmsMvcPartialControlParameters();
                        RenderMvcPartial(mvcPartialParameters.PartialViewName, mvcPartialParameters.PartialViewModel, helper);
                        break;
                    case PageControlTypeKind.ModulePart:
                        mvcPartialParameters = pageControl.GetCmsMvcPartialControlParameters();
                        var controller = (string) mvcPartialParameters.ActionController;
                        var action = (string) mvcPartialParameters.ActionName;
                        if (!string.IsNullOrEmpty(controller) && !string.IsNullOrEmpty(action))
                        {
                            RenderMvcAction(controller, action, mvcPartialParameters.PartialViewModel, helper, CmsPolicy.FrontEndArea);
                        }
                        else
                        {
                            RenderMvcPartial(mvcPartialParameters.PartialViewName, mvcPartialParameters.PartialViewModel, helper);
                        }
                        break;
                    case PageControlTypeKind.ContentModule:                        
                    case PageControlTypeKind.MvcAction:
                        var mvcActionParameters = pageControl.GetCmsMvcPartialControlParameters();
                        string areaName = pageControl.CommonPageControlType == PageControlTypeKind.MvcAction
                                              ? CmsPolicy.FrontEndArea
                                              : ContentModuleArea.ContentAreaName;
                        RenderMvcAction(mvcActionParameters.ActionController, mvcActionParameters.ActionName, mvcActionParameters.PartialViewModel, helper, areaName);
                        break;
                }
            }
            catch (Exception ex)
            {
                if (!helper.ViewContext.HttpContext.IsDebuggingEnabled)
                {
                    RenderControlError(pageControl, writer, ex); 
                }
                else
                {
                    throw;
                }
            }
        }

        private static void RenderControlError(CmsPageControl pageControl, TextWriter writer, Exception ex)
        {
            var tagBuilder = new TagBuilder("div");

            tagBuilder.MergeAttribute("style", "color: #ff0000; border: 1px dashed #ff0000; padding: 5px;", true);
            tagBuilder.InnerHtml =
                string.Format("<p>Fuse8.ContentManagementFramework runtime error: <br/>Error loading control. PageControlId = {0}; <br/> Exception = {1}</p>",
                    pageControl.Id,
                    ex.InnerException != null ? string.Format("{0} : {1}", ex.Message, ex.InnerException) : ex.Message);

            writer.Write(tagBuilder.ToString(TagRenderMode.Normal));
        }
        
        private static void RenderContent(string contentBody, TextWriter writer)
        {
            writer.WriteLine(contentBody);
        }

        private static void RenderMvcMedia(string contentValue, TextWriter writer)
        { }

        private static void RenderMvcPartial(string view, object model, HtmlHelper helper)
        {
            if (!string.IsNullOrWhiteSpace(view))
            {
                helper.RenderPartial(view, model);
            }
        }

        private static void RenderMvcAction(string controller, string action, object model, HtmlHelper helper, string areaName)
        {
            if (!string.IsNullOrWhiteSpace(controller) && !string.IsNullOrWhiteSpace(action))
            {
                var routeValues = GetRouteValuesFromModel(model);

                if (!string.IsNullOrWhiteSpace(areaName))
                {
                    routeValues = MergeDictionaries(routeValues, new RouteValueDictionary(new { area = areaName }));
                }

                helper.RenderAction(action, controller, routeValues);
            }
        }

        internal static RouteValueDictionary GetRouteValuesFromModel(object model)
        {
            RouteValueDictionary routeValues;
            if (CanModelBeTransformedToRouteAsIs(model))
            {
                routeValues = new RouteValueDictionary(model);
            }
            else
            {
                routeValues = new RouteValueDictionary(new {model});
            }
            return routeValues;
        }

        private static bool CanModelBeTransformedToRouteAsIs(object model)
        {
            bool result = true;
            if (model != null)
            {
                var type = model.GetType();
                result = !type.IsEnum && !type.IsPrimitive && type != typeof (string);
            }

            return result;
        }

        private static void RenderTemplateRegionPreview(string regionName, TextWriter writer)
        {
            var tagBuilder = new TagBuilder("div");

            tagBuilder.MergeAttribute("style", "color: #ff0000; border: 1px dashed #ff0000; padding: 5px;", true);
            tagBuilder.SetInnerText(regionName);

            writer.Write(tagBuilder.ToString(TagRenderMode.Normal));
        }

        private static RouteValueDictionary MergeDictionaries(params RouteValueDictionary[] dictionaries)
        {
            // Merge existing route values with the user provided values
            var result = new RouteValueDictionary();

            foreach (RouteValueDictionary dictionary in dictionaries.Where(d => d != null))
            {
                foreach (KeyValuePair<string, object> kvp in dictionary)
                {
                    if (!result.ContainsKey(kvp.Key))
                    {
                        result.Add(kvp.Key, kvp.Value);
                    }
                }
            }

            return result;
        }
    }
}
